/*!
 * Copyright 2012, Chris Wanstrath
 * Released under the MIT License
 * https://github.com/defunkt/jquery-pjax
 */

(function($){

	// When called on a container with a selector, fetches the href with
	// ajax into the container or with the data-pjax attribute on the link
	// itself.
	//
	// Tries to make sure the back button and ctrl+click work the way
	// you'd expect.
	//
	// Exported as $.fn.pjax
	//
	// Accepts a jQuery ajax options object that may include these
	// pjax specific options:
	//
	//
	// container - String selector for the element where to place the response body.
	//      push - Whether to pushState the URL. Defaults to true (of course).
	//   replace - Want to use replaceState instead? That's cool.
	//
	// For convenience the second parameter can be either the container or
	// the options object.
	//
	// Returns the jQuery object
	function fnPjax(selector, container, options) {
	  options = optionsFor(container, options)
	  return this.on('click.pjax', selector, function(event) {
		var opts = options
		if (!opts.container) {
		  opts = $.extend({}, options)
		  opts.container = $(this).attr('data-pjax')
		}
		handleClick(event, opts)
	  })
	}
	
	// Public: pjax on click handler
	//
	// Exported as $.pjax.click.
	//
	// event   - "click" jQuery.Event
	// options - pjax options
	//
	// Examples
	//
	//   $(document).on('click', 'a', $.pjax.click)
	//   // is the same as
	//   $(document).pjax('a')
	//
	// Returns nothing.
	function handleClick(event, container, options) {
	  options = optionsFor(container, options)
	
	  var link = event.currentTarget
	  var $link = $(link)
	
	  if (link.tagName.toUpperCase() !== 'A')
		throw "$.fn.pjax or $.pjax.click requires an anchor element"
	
	  // Middle click, cmd click, and ctrl click should open
	  // links in a new tab as normal.
	  if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey )
		return
	
	  // Ignore cross origin links
	  if ( location.protocol !== link.protocol || location.hostname !== link.hostname )
		return
	
	  // Ignore case when a hash is being tacked on the current URL
	  if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) )
		return
	
	  // Ignore event with default prevented
	  if (event.isDefaultPrevented())
		return
	
	  var defaults = {
		url: link.href,
		container: $link.attr('data-pjax'),
		target: link
	  }
	
	  var opts = $.extend({}, defaults, options)
	  var clickEvent = $.Event('pjax:click')
	  $link.trigger(clickEvent, [opts])
	
	  if (!clickEvent.isDefaultPrevented()) {
		pjax(opts)
		event.preventDefault()
		$link.trigger('pjax:clicked', [opts])
	  }
	}
	
	// Public: pjax on form submit handler
	//
	// Exported as $.pjax.submit
	//
	// event   - "click" jQuery.Event
	// options - pjax options
	//
	// Examples
	//
	//  $(document).on('submit', 'form', function(event) {
	//    $.pjax.submit(event, '[data-pjax-container]')
	//  })
	//
	// Returns nothing.
	function handleSubmit(event, container, options) {
	  options = optionsFor(container, options)
	
	  var form = event.currentTarget
	  var $form = $(form)
	
	  if (form.tagName.toUpperCase() !== 'FORM')
		throw "$.pjax.submit requires a form element"
	
	  var defaults = {
		type: ($form.attr('method') || 'GET').toUpperCase(),
		url: $form.attr('action'),
		container: $form.attr('data-pjax'),
		target: form
	  }
	
	  if (defaults.type !== 'GET' && window.FormData !== undefined) {
		defaults.data = new FormData(form)
		defaults.processData = false
		defaults.contentType = false
	  } else {
		// Can't handle file uploads, exit
		if ($form.find(':file').length) {
		  return
		}
	
		// Fallback to manually serializing the fields
		defaults.data = $form.serializeArray()
	  }
	
	  pjax($.extend({}, defaults, options))
	
	  event.preventDefault()
	}
	
	// Loads a URL with ajax, puts the response body inside a container,
	// then pushState()'s the loaded URL.
	//
	// Works just like $.ajax in that it accepts a jQuery ajax
	// settings object (with keys like url, type, data, etc).
	//
	// Accepts these extra keys:
	//
	// container - String selector for where to stick the response body.
	//      push - Whether to pushState the URL. Defaults to true (of course).
	//   replace - Want to use replaceState instead? That's cool.
	//
	// Use it just like $.ajax:
	//
	//   var xhr = $.pjax({ url: this.href, container: '#main' })
	//   console.log( xhr.readyState )
	//
	// Returns whatever $.ajax returns.
	function pjax(options) {
	  options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
	
	  if ($.isFunction(options.url)) {
		options.url = options.url()
	  }
	
	  var hash = parseURL(options.url).hash
	
	  var containerType = $.type(options.container)
	  if (containerType !== 'string') {
		throw "expected string value for 'container' option; got " + containerType
	  }
	  var context = options.context = $(options.container)
	  if (!context.length) {
		throw "the container selector '" + options.container + "' did not match anything"
	  }
	
	  // We want the browser to maintain two separate internal caches: one
	  // for pjax'd partial page loads and one for normal page loads.
	  // Without adding this secret parameter, some browsers will often
	  // confuse the two.
	  if (!options.data) options.data = {}
	  if ($.isArray(options.data)) {
		options.data.push({name: '_pjax', value: options.container})
	  } else {
		options.data._pjax = options.container
	  }
	
	  function fire(type, args, props) {
		if (!props) props = {}
		props.relatedTarget = options.target
		var event = $.Event(type, props)
		context.trigger(event, args)
		return !event.isDefaultPrevented()
	  }
	
	  var timeoutTimer
	
	  options.beforeSend = function(xhr, settings) {
		// No timeout for non-GET requests
		// Its not safe to request the resource again with a fallback method.
		if (settings.type !== 'GET') {
		  settings.timeout = 0
		}
	
		xhr.setRequestHeader('X-PJAX', 'true')
		xhr.setRequestHeader('X-PJAX-Container', options.container)
	
		if (!fire('pjax:beforeSend', [xhr, settings]))
		  return false
	
		if (settings.timeout > 0) {
		  timeoutTimer = setTimeout(function() {
			if (fire('pjax:timeout', [xhr, options]))
			  xhr.abort('timeout')
		  }, settings.timeout)
	
		  // Clear timeout setting so jquerys internal timeout isn't invoked
		  settings.timeout = 0
		}
	
		var url = parseURL(settings.url)
		if (hash) url.hash = hash
		options.requestUrl = stripInternalParams(url)
	  }
	
	  options.complete = function(xhr, textStatus) {
		if (timeoutTimer)
		  clearTimeout(timeoutTimer)
	
		fire('pjax:complete', [xhr, textStatus, options])
	
		fire('pjax:end', [xhr, options])
	  }
	
	  options.error = function(xhr, textStatus, errorThrown) {
		var container = extractContainer("", xhr, options)
	
		var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options])
		if (options.type == 'GET' && textStatus !== 'abort' && allowed) {
		  locationReplace(container.url)
		}
	  }
	
	  options.success = function(data, status, xhr) {
		var previousState = pjax.state
	
		// If $.pjax.defaults.version is a function, invoke it first.
		// Otherwise it can be a static string.
		var currentVersion = typeof $.pjax.defaults.version === 'function' ?
		  $.pjax.defaults.version() :
		  $.pjax.defaults.version
	
		var latestVersion = xhr.getResponseHeader('X-PJAX-Version')
	
		var container = extractContainer(data, xhr, options)
	
		var url = parseURL(container.url)
		if (hash) {
		  url.hash = hash
		  container.url = url.href
		}
	
		// If there is a layout version mismatch, hard load the new url
		if (currentVersion && latestVersion && currentVersion !== latestVersion) {
		  locationReplace(container.url)
		  return
		}
	
		// If the new response is missing a body, hard load the page
		if (!container.contents) {
		  locationReplace(container.url)
		  return
		}
	
		pjax.state = {
		  id: options.id || uniqueId(),
		  url: container.url,
		  title: container.title,
		  container: options.container,
		  fragment: options.fragment,
		  timeout: options.timeout
		}
	
		if (options.push || options.replace) {
		  window.history.replaceState(pjax.state, container.title, container.url)
		}
	
		// Only blur the focus if the focused element is within the container.
		var blurFocus = $.contains(context, document.activeElement)
	
		// Clear out any focused controls before inserting new page contents.
		if (blurFocus) {
		  try {
			document.activeElement.blur()
		  } catch (e) { /* ignore */ }
		}
	
		if (container.title) document.title = container.title
	
		fire('pjax:beforeReplace', [container.contents, options], {
		  state: pjax.state,
		  previousState: previousState
		})
		context.html(container.contents)
	
		// FF bug: Won't autofocus fields that are inserted via JS.
		// This behavior is incorrect. So if theres no current focus, autofocus
		// the last field.
		//
		// http://www.w3.org/html/wg/drafts/html/master/forms.html
		var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0]
		if (autofocusEl && document.activeElement !== autofocusEl) {
		  autofocusEl.focus()
		}
	
		executeScriptTags(container.scripts)
	
		var scrollTo = options.scrollTo
	
		// Ensure browser scrolls to the element referenced by the URL anchor
		if (hash) {
		  var name = decodeURIComponent(hash.slice(1))
		  var target = document.getElementById(name) || document.getElementsByName(name)[0]
		  if (target) scrollTo = $(target).offset().top
		}
	
		if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo)
	
		fire('pjax:success', [data, status, xhr, options])
	  }
	
	
	  // Initialize pjax.state for the initial page load. Assume we're
	  // using the container and options of the link we're loading for the
	  // back button to the initial page. This ensures good back button
	  // behavior.
	  if (!pjax.state) {
		pjax.state = {
		  id: uniqueId(),
		  url: window.location.href,
		  title: document.title,
		  container: options.container,
		  fragment: options.fragment,
		  timeout: options.timeout
		}
		window.history.replaceState(pjax.state, document.title)
	  }
	
	  // Cancel the current request if we're already pjaxing
	  abortXHR(pjax.xhr)
	
	  pjax.options = options
	  var xhr = pjax.xhr = $.ajax(options)
	
	  if (xhr.readyState > 0) {
		if (options.push && !options.replace) {
		  // Cache current container element before replacing it
		  cachePush(pjax.state.id, [options.container, cloneContents(context)])
	
		  window.history.pushState(null, "", options.requestUrl)
		}
	
		fire('pjax:start', [xhr, options])
		fire('pjax:send', [xhr, options])
	  }
	
	  return pjax.xhr
	}
	
	// Public: Reload current page with pjax.
	//
	// Returns whatever $.pjax returns.
	function pjaxReload(container, options) {
	  var defaults = {
		url: window.location.href,
		push: false,
		replace: true,
		scrollTo: false
	  }
	
	  return pjax($.extend(defaults, optionsFor(container, options)))
	}
	
	// Internal: Hard replace current state with url.
	//
	// Work for around WebKit
	//   https://bugs.webkit.org/show_bug.cgi?id=93506
	//
	// Returns nothing.
	function locationReplace(url) {
	  window.history.replaceState(null, "", pjax.state.url)
	  window.location.replace(url)
	}
	
	
	var initialPop = true
	var initialURL = window.location.href
	var initialState = window.history.state
	
	// Initialize $.pjax.state if possible
	// Happens when reloading a page and coming forward from a different
	// session history.
	if (initialState && initialState.container) {
	  pjax.state = initialState
	}
	
	// Non-webkit browsers don't fire an initial popstate event
	if ('state' in window.history) {
	  initialPop = false
	}
	
	// popstate handler takes care of the back and forward buttons
	//
	// You probably shouldn't use pjax on pages with other pushState
	// stuff yet.
	function onPjaxPopstate(event) {
	
	  // Hitting back or forward should override any pending PJAX request.
	  if (!initialPop) {
		abortXHR(pjax.xhr)
	  }
	
	  var previousState = pjax.state
	  var state = event.state
	  var direction
	
	  if (state && state.container) {
		// When coming forward from a separate history session, will get an
		// initial pop with a state we are already at. Skip reloading the current
		// page.
		if (initialPop && initialURL == state.url) return
	
		if (previousState) {
		  // If popping back to the same state, just skip.
		  // Could be clicking back from hashchange rather than a pushState.
		  if (previousState.id === state.id) return
	
		  // Since state IDs always increase, we can deduce the navigation direction
		  direction = previousState.id < state.id ? 'forward' : 'back'
		}
	
		var cache = cacheMapping[state.id] || []
		var containerSelector = cache[0] || state.container
		var container = $(containerSelector), contents = cache[1]
	
		if (container.length) {
		  if (previousState) {
			// Cache current container before replacement and inform the
			// cache which direction the history shifted.
			cachePop(direction, previousState.id, [containerSelector, cloneContents(container)])
		  }
	
		  var popstateEvent = $.Event('pjax:popstate', {
			state: state,
			direction: direction
		  })
		  container.trigger(popstateEvent)
	
		  var options = {
			id: state.id,
			url: state.url,
			container: containerSelector,
			push: false,
			fragment: state.fragment,
			timeout: state.timeout,
			scrollTo: false
		  }
	
		  if (contents) {
			container.trigger('pjax:start', [null, options])
	
			pjax.state = state
			if (state.title) document.title = state.title
			var beforeReplaceEvent = $.Event('pjax:beforeReplace', {
			  state: state,
			  previousState: previousState
			})
			container.trigger(beforeReplaceEvent, [contents, options])
			container.html(contents)
	
			container.trigger('pjax:end', [null, options])
		  } else {
			pjax(options)
		  }
	
		  // Force reflow/relayout before the browser tries to restore the
		  // scroll position.
		  container[0].offsetHeight // eslint-disable-line no-unused-expressions
		} else {
		  locationReplace(location.href)
		}
	  }
	  initialPop = false
	}
	
	// Fallback version of main pjax function for browsers that don't
	// support pushState.
	//
	// Returns nothing since it retriggers a hard form submission.
	function fallbackPjax(options) {
	  var url = $.isFunction(options.url) ? options.url() : options.url,
		  method = options.type ? options.type.toUpperCase() : 'GET'
	
	  var form = $('<form>', {
		method: method === 'GET' ? 'GET' : 'POST',
		action: url,
		style: 'display:none'
	  })
	
	  if (method !== 'GET' && method !== 'POST') {
		form.append($('<input>', {
		  type: 'hidden',
		  name: '_method',
		  value: method.toLowerCase()
		}))
	  }
	
	  var data = options.data
	  if (typeof data === 'string') {
		$.each(data.split('&'), function(index, value) {
		  var pair = value.split('=')
		  form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
		})
	  } else if ($.isArray(data)) {
		$.each(data, function(index, value) {
		  form.append($('<input>', {type: 'hidden', name: value.name, value: value.value}))
		})
	  } else if (typeof data === 'object') {
		var key
		for (key in data)
		  form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))
	  }
	
	  $(document.body).append(form)
	  form.submit()
	}
	
	// Internal: Abort an XmlHttpRequest if it hasn't been completed,
	// also removing its event handlers.
	function abortXHR(xhr) {
	  if ( xhr && xhr.readyState < 4) {
		xhr.onreadystatechange = $.noop
		xhr.abort()
	  }
	}
	
	// Internal: Generate unique id for state object.
	//
	// Use a timestamp instead of a counter since ids should still be
	// unique across page loads.
	//
	// Returns Number.
	function uniqueId() {
	  return (new Date).getTime()
	}
	
	function cloneContents(container) {
	  var cloned = container.clone()
	  // Unmark script tags as already being eval'd so they can get executed again
	  // when restored from cache. HAXX: Uses jQuery internal method.
	  cloned.find('script').each(function(){
		if (!this.src) $._data(this, 'globalEval', false)
	  })
	  return cloned.contents()
	}
	
	// Internal: Strip internal query params from parsed URL.
	//
	// Returns sanitized url.href String.
	function stripInternalParams(url) {
	  url.search = url.search.replace(/([?&])(_pjax|_)=[^&]*/g, '').replace(/^&/, '')
	  return url.href.replace(/\?($|#)/, '$1')
	}
	
	// Internal: Parse URL components and returns a Locationish object.
	//
	// url - String URL
	//
	// Returns HTMLAnchorElement that acts like Location.
	function parseURL(url) {
	  var a = document.createElement('a')
	  a.href = url
	  return a
	}
	
	// Internal: Return the `href` component of given URL object with the hash
	// portion removed.
	//
	// location - Location or HTMLAnchorElement
	//
	// Returns String
	function stripHash(location) {
	  return location.href.replace(/#.*/, '')
	}
	
	// Internal: Build options Object for arguments.
	//
	// For convenience the first parameter can be either the container or
	// the options object.
	//
	// Examples
	//
	//   optionsFor('#container')
	//   // => {container: '#container'}
	//
	//   optionsFor('#container', {push: true})
	//   // => {container: '#container', push: true}
	//
	//   optionsFor({container: '#container', push: true})
	//   // => {container: '#container', push: true}
	//
	// Returns options Object.
	function optionsFor(container, options) {
	  if (container && options) {
		options = $.extend({}, options)
		options.container = container
		return options
	  } else if ($.isPlainObject(container)) {
		return container
	  } else {
		return {container: container}
	  }
	}
	
	// Internal: Filter and find all elements matching the selector.
	//
	// Where $.fn.find only matches descendants, findAll will test all the
	// top level elements in the jQuery object as well.
	//
	// elems    - jQuery object of Elements
	// selector - String selector to match
	//
	// Returns a jQuery object.
	function findAll(elems, selector) {
	  return elems.filter(selector).add(elems.find(selector))
	}
	
	function parseHTML(html) {
	  return $.parseHTML(html, document, true)
	}
	
	// Internal: Extracts container and metadata from response.
	//
	// 1. Extracts X-PJAX-URL header if set
	// 2. Extracts inline <title> tags
	// 3. Builds response Element and extracts fragment if set
	//
	// data    - String response data
	// xhr     - XHR response
	// options - pjax options Object
	//
	// Returns an Object with url, title, and contents keys.
	function extractContainer(data, xhr, options) {
	  var obj = {}, fullDocument = /<html/i.test(data)
	
	  // Prefer X-PJAX-URL header if it was set, otherwise fallback to
	  // using the original requested url.
	  var serverUrl = xhr.getResponseHeader('X-PJAX-URL')
	  obj.url = serverUrl ? stripInternalParams(parseURL(serverUrl)) : options.requestUrl
	
	  var $head, $body
	  // Attempt to parse response html into elements
	  if (fullDocument) {
		$body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]))
		var head = data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)
		$head = head != null ? $(parseHTML(head[0])) : $body
	  } else {
		$head = $body = $(parseHTML(data))
	  }
	
	  // If response data is empty, return fast
	  if ($body.length === 0)
		return obj
	
	  // If there's a <title> tag in the header, use it as
	  // the page's title.
	  obj.title = findAll($head, 'title').last().text()
	
	  if (options.fragment) {
		var $fragment = $body
		// If they specified a fragment, look for it in the response
		// and pull it out.
		if (options.fragment !== 'body') {
		  $fragment = findAll($fragment, options.fragment).first()
		}
	
		if ($fragment.length) {
		  obj.contents = options.fragment === 'body' ? $fragment : $fragment.contents()
	
		  // If there's no title, look for data-title and title attributes
		  // on the fragment
		  if (!obj.title)
			obj.title = $fragment.attr('title') || $fragment.data('title')
		}
	
	  } else if (!fullDocument) {
		obj.contents = $body
	  }
	
	  // Clean up any <title> tags
	  if (obj.contents) {
		// Remove any parent title elements
		obj.contents = obj.contents.not(function() { return $(this).is('title') })
	
		// Then scrub any titles from their descendants
		obj.contents.find('title').remove()
	
		// Gather all script[src] elements
		obj.scripts = findAll(obj.contents, 'script[src]').remove()
		obj.contents = obj.contents.not(obj.scripts)
	  }
	
	  // Trim any whitespace off the title
	  if (obj.title) obj.title = $.trim(obj.title)
	
	  return obj
	}
	
	// Load an execute scripts using standard script request.
	//
	// Avoids jQuery's traditional $.getScript which does a XHR request and
	// globalEval.
	//
	// scripts - jQuery object of script Elements
	//
	// Returns nothing.
	function executeScriptTags(scripts) {
	  if (!scripts) return
	
	  var existingScripts = $('script[src]')
	
	  scripts.each(function() {
		var src = this.src
		var matchedScripts = existingScripts.filter(function() {
		  return this.src === src
		})
		if (matchedScripts.length) return
	
		var script = document.createElement('script')
		var type = $(this).attr('type')
		if (type) script.type = type
		script.src = $(this).attr('src')
		document.head.appendChild(script)
	  })
	}
	
	// Internal: History DOM caching class.
	var cacheMapping      = {}
	var cacheForwardStack = []
	var cacheBackStack    = []
	
	// Push previous state id and container contents into the history
	// cache. Should be called in conjunction with `pushState` to save the
	// previous container contents.
	//
	// id    - State ID Number
	// value - DOM Element to cache
	//
	// Returns nothing.
	function cachePush(id, value) {
	  cacheMapping[id] = value
	  cacheBackStack.push(id)
	
	  // Remove all entries in forward history stack after pushing a new page.
	  trimCacheStack(cacheForwardStack, 0)
	
	  // Trim back history stack to max cache length.
	  trimCacheStack(cacheBackStack, pjax.defaults.maxCacheLength)
	}
	
	// Shifts cache from directional history cache. Should be
	// called on `popstate` with the previous state id and container
	// contents.
	//
	// direction - "forward" or "back" String
	// id        - State ID Number
	// value     - DOM Element to cache
	//
	// Returns nothing.
	function cachePop(direction, id, value) {
	  var pushStack, popStack
	  cacheMapping[id] = value
	
	  if (direction === 'forward') {
		pushStack = cacheBackStack
		popStack  = cacheForwardStack
	  } else {
		pushStack = cacheForwardStack
		popStack  = cacheBackStack
	  }
	
	  pushStack.push(id)
	  id = popStack.pop()
	  if (id) delete cacheMapping[id]
	
	  // Trim whichever stack we just pushed to to max cache length.
	  trimCacheStack(pushStack, pjax.defaults.maxCacheLength)
	}
	
	// Trim a cache stack (either cacheBackStack or cacheForwardStack) to be no
	// longer than the specified length, deleting cached DOM elements as necessary.
	//
	// stack  - Array of state IDs
	// length - Maximum length to trim to
	//
	// Returns nothing.
	function trimCacheStack(stack, length) {
	  while (stack.length > length)
		delete cacheMapping[stack.shift()]
	}
	
	// Public: Find version identifier for the initial page load.
	//
	// Returns String version or undefined.
	function findVersion() {
	  return $('meta').filter(function() {
		var name = $(this).attr('http-equiv')
		return name && name.toUpperCase() === 'X-PJAX-VERSION'
	  }).attr('content')
	}
	
	// Install pjax functions on $.pjax to enable pushState behavior.
	//
	// Does nothing if already enabled.
	//
	// Examples
	//
	//     $.pjax.enable()
	//
	// Returns nothing.
	function enable() {
	  $.fn.pjax = fnPjax
	  $.pjax = pjax
	  $.pjax.enable = $.noop
	  $.pjax.disable = disable
	  $.pjax.click = handleClick
	  $.pjax.submit = handleSubmit
	  $.pjax.reload = pjaxReload
	  $.pjax.defaults = {
		timeout: 650,
		push: true,
		replace: false,
		type: 'GET',
		dataType: 'html',
		scrollTo: 0,
		maxCacheLength: 20,
		version: findVersion
	  }
	  $(window).on('popstate.pjax', onPjaxPopstate)
	}
	
	// Disable pushState behavior.
	//
	// This is the case when a browser doesn't support pushState. It is
	// sometimes useful to disable pushState for debugging on a modern
	// browser.
	//
	// Examples
	//
	//     $.pjax.disable()
	//
	// Returns nothing.
	function disable() {
	  $.fn.pjax = function() { return this }
	  $.pjax = fallbackPjax
	  $.pjax.enable = enable
	  $.pjax.disable = $.noop
	  $.pjax.click = $.noop
	  $.pjax.submit = $.noop
	  $.pjax.reload = function() { window.location.reload() }
	
	  $(window).off('popstate.pjax', onPjaxPopstate)
	}
	
	
	// Add the state property to jQuery's event object so we can use it in
	// $(window).bind('popstate')
	if ($.event.props && $.inArray('state', $.event.props) < 0) {
	  $.event.props.push('state')
	} else if (!('state' in $.Event.prototype)) {
	  $.event.addProp('state')
	}
	
	// Is pjax supported by this browser?
	$.support.pjax =
	  window.history && window.history.pushState && window.history.replaceState &&
	  // pushState isn't reliable on iOS until 5.
	  !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)
	
	if ($.support.pjax) {
	  enable()
	} else {
	  disable()
	}
	
	})(jQuery)